package com.geline.easyexcel.listener;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.geline.easyexcel.domain.ExcelError;
import com.geline.easyexcel.interfaces.ExcelValidFunction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanMap;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Consumer;

/**
 * Excel导入监听器，使用分批插入
 *
 * @author cheng-qiang
 * @date 2022年11月17日11:11
 */
@Slf4j
public class JdbcEventListener<T> extends AnalysisEventListener<T> {
    protected Map<String, List<ExcelError>> excelErrorMap = new HashMap<>();
    protected Class rowClass;
    //如果校验通过消费解析得到的excel数据
    protected final Consumer<Collection> batchConsumer;
    //导入时页面传入的关联参数
    protected Map<String, Object> example;
    protected final ExcelValidFunction validFunction;
    //解析数据的临时存储容器
    protected List<T> list = new ArrayList<>();

    public Map<String, List<ExcelError>> getExcelErrorMap() {
        return excelErrorMap;
    }

    /**
     * @param batchConsumer  Excel解析结果批量消费工具，可实现为写入数据库等消费操作
     */
    public JdbcEventListener(Class rowClass, Consumer batchConsumer, Map<String, Object> example, ExcelValidFunction function) {
        this.rowClass = rowClass;
        this.batchConsumer = batchConsumer;
        this.example = example;
        this.validFunction = function;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        int count = 0;
        // 获取数据实体的字段列表
        Field[] fields = this.rowClass.getDeclaredFields();
        // 遍历字段进行判断
        for (Field field : fields) {
            // 获取当前字段上的ExcelProperty注解信息
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation != null) {
                ++count;
                String fieldHead = annotation.value()[0];
                if(StrUtil.isNotBlank(fieldHead)){
                    String headName = headMap.get(annotation.index());
                    if (!headName.equals(fieldHead)) {
                        String tip = String.format("模板错误，请检查导入模板(%s不匹配%s)", headName, fieldHead);
                        log.warn(tip);
                        throw new RuntimeException(tip);
                    }
                }
            }
        }
        // 判断用户导入表格的标题头是否完全符合模板
        if (count != headMap.size()) {
            log.warn("模板错误，请检查导入模板(表头数量不对) : @ExcelProperty={}, Excel-header={}", count, headMap.size());
            throw new RuntimeException("模板错误，请检查导入模板(表头数量不对)");
        }
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        list.clear();
        if(exception instanceof ExcelDataConvertException){
            ExcelDataConvertException convertException=(ExcelDataConvertException) exception;

            int rowIndex = convertException.getRowIndex();
            int colIndex = convertException.getColumnIndex();
            String msg = "数据格式不正确";
            ExcelError excelError = new ExcelError(rowIndex, colIndex, msg);

            List<ExcelError> errorList = new ArrayList();
            if(excelErrorMap != null){
                if(excelErrorMap.containsKey(String.valueOf(rowIndex))){
                    errorList = excelErrorMap.get(String.valueOf(rowIndex));
                    if(errorList != null && !errorList.isEmpty()){
                        errorList.stream().filter(item-> item.getColIndex() == colIndex).forEach(item -> {
                            item.setMsg(msg);
                        });
                    }else{
                        errorList = new ArrayList();
                        errorList.add(excelError);
                    }
                }else{
                    errorList.add(excelError);
                }
            }else{
                excelErrorMap = new HashMap<>();
                errorList.add(excelError);
            }
            excelErrorMap.put(String.valueOf(rowIndex), errorList);
        }else{
            throw exception;
        }
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        //当前读取数据行号
        Integer rowIndex = context.readRowHolder().getRowIndex();

        log.info("Excel数据读取，当前数据所在行号 Excel-RowIndex={}", rowIndex);

        List<ExcelError> valid = this.validFunction.apply(rowIndex, data);
        if (valid != null && !valid.isEmpty()) {
            //当前行数据检验失败
            excelErrorMap.put(String.valueOf(rowIndex), valid);
        }else {
            //设置关联参数
            if(example !=null && !example.isEmpty()){
                BeanMap.create(data).putAll(example);
            }
            list.add(data);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (!list.isEmpty()) {
            this.batchConsumer.accept(list);
        }
    }
}
